package  
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Sprite;
	import flash.events.*;

	public class Mandelbrot extends Sprite 
	{
		//ofset for the zoom
		private var offsetX:Number = 0;
		private var offsetY:Number = 0;
		//current scale of the zoom
		private var scale:Number = 1;
		//array of colors to use for iterations
		private var colors:Array = [];
		//size of the view window
		private var xSize:Number = 3;
		private var ySize:Number = 2.4;
		//the amount of colors to use
		private var colorSegments:int;
		//the bitmapdata where the fractal will be drawn
		public var rb:BitmapData;
		//the bitmap that will be added to the view
		private var bmp:Bitmap = new Bitmap();
		//the iteration depth (detail of the set)
		private var maxIter:int;
		//the size of the zoombox relative to the screen
		public var zoomFactor:Number = 0.3;
		
		public function Mandelbrot(w:int,h:int,_maxIter:int=500) 
		{
			maxIter = _maxIter;
			colorSegments = maxIter / 3;
			for (var c:int = 0; c < colorSegments; c++) 
			{
				colors[c] =	rgb(Math.random()*128,
							Math.random() * 128,
							(255 / colorSegments * c));
			}			
			rb = new BitmapData(w, h, false, 0);
			
			drawMandel();				
			addChild(bmp);
		}
		public function zoomIn(e:MouseEvent):void
		{		
			applyZoom();
		}
		public function zoom(amount:Number=1.1):void
		{
			zoomFactor = amount;
			applyZoom();

		}
		private function applyZoom():void
		{
			setOffset(mouseX, mouseY);
			drawMandel();			
		}

		private function setOffset(x:Number,y:Number):void
		{
			var nx:Number = offsetX + ((x / rb.width)) * (scale * xSize);
			var ny:Number = offsetY + ((y / rb.height)) * (scale * ySize);			
			scale *= zoomFactor;
			offsetX = nx - (scale * xSize * 0.5);
			offsetY = ny - (scale * ySize * 0.5);
		}
		

		public function cycle(direction:int=0):void
		{
			for (var i:int = 0; i < bmp.width; i++) 
			{
				for (var j:int = 0; j < bmp.height; j++) 
				{
					var pxl:uint = bmp.bitmapData.getPixel(i, j);
					if(pxl>0)
						bmp.bitmapData.setPixel(i, j, getNextColor(pxl,direction));
				}				
			}
			if (direction == 0)
				colors.push(colors.shift());
			else
				colors.unshift(colors.pop());
		}		
		private function drawMandel():void
		{		
			var realMin:Number = (-2.0 + offsetX);
			var realMax:Number = realMin + (xSize * scale);
			var imagMin:Number = -1.2 + offsetY;			
			var imagMax:Number = imagMin + (ySize * scale);
			
			var realFactor:Number = (realMax - realMin) / (rb.width - 1);
			var imagFactor:Number = (imagMax - imagMin) / (rb.height - 1);			
			for (var i:int = 0; i < rb.width; i++) 
			{				
				for (var j:int = 0; j < rb.height; j++) 
				{
					var realC:Number = realMin + i * realFactor;
					var imagC:Number = imagMin + j * imagFactor;
					var realZ:Number = realC;
					var imagZ:Number = imagC;
					var iter:int = 0;
					while (realZ * realZ + imagZ * imagZ < 4 && iter < maxIter)
					{
						var imagZ2:Number = imagZ * imagZ;
						imagZ = 2 * imagZ * realZ + imagC;
						realZ = realZ * realZ - imagZ2 +realC;						
						iter++;
					}					
					rb.setPixel(i, j, (iter<maxIter)?colors[(iter > colorSegments)?iter % colorSegments:iter]:0x000000);
				}				
			}

			bmp.bitmapData = rb;
		}		
		private function rgb(r:uint, g:uint, b:uint):uint{
			var hex:uint = (r << 16 | g << 8 | b);
			return hex;
		}

		private function getNextColor(c:uint,d:int):uint
		{
			for (var i:int = 0; i < colorSegments-1; i++) 
			{
				if (c == colors[i])
				{	
					if(d==0)
						return colors[i + 1];					
					else
					{
						if (i == 0)
							return colors[colorSegments - 1];
						else
							return colors[i - 1];
					}
				}
			}
			return (d==0)?colors[0]:colors[colorSegments-1];
		}
	}
	
}